pics added in readme
[Cyberpunk Pendant] / SOFTWARE / pendent.c
1 /*
2  * CyberHeart, AVR ATtiny10 cellular automation micro-engine
3  * Copyright © 2021 Dmitry Shalnoff [interplaymedium.org]
4  * Licensed under the Apache License, Version 2.0
5 */
6
7 #define F_CPU 8000000UL         // define CPU speed. actual clock speed is set using CLKPSR prescaler (see main()
8
9 // ---------------------- main settings ------------------------
10
11 // #define UART         1       // UART on PB1, for debugging purposes. due the lack of memory, LED PWM and watchdog delay will be disabled
12
13 // #define      RULE            22      // rule 22 look better, 30 gives shorter sequences and looks like giving longer 'tales' (test needed)
14 #define LEN             8       // perimeter of the universe (bytes) (maximum for attiny10)
15 #define LOW_STAT_THRS   6       // 6    // 13   // low threshold for silent 'messaging', to make LED more "talkative" :) Check statistics for your RULE and change it
16
17 // #define BATT_LOW_LIMIT       231     // 231  // ADC battery meseurement limit for 'charging' message, 255 - minimum
18
19 // -------------------------------------------------------------
20
21 #include <avr/io.h>
22
23 #ifdef UART
24         #include <util/delay.h>
25 #endif
26
27 #include <avr/interrupt.h>
28 #include <avr/pgmspace.h>
29
30 #include <avr/sleep.h>
31 #include <util/atomic.h>
32 #include <avr/wdt.h>
33
34 #define ADC PB0         
35 #define SND PB0         
36 #define LED PB2         // BLUE
37 #define LED2 PB1        // RED
38
39 #define EXP_LEN         10      // EXP, LED PWM glowing, number of steps
40
41 uint8_t         uni[ LEN ];     // 1D compact manifold universe :)
42 uint8_t         mode = 0, mode_init=0;
43
44 #define MODE_BLUE       0
45 #define MODE_RED        1
46 #define MODE_BLUE_RED   2
47 #define MODE_ROTATE     3
48
49         uint8_t         i = LEN-1, tmp, triade;
50         uint8_t         cnt = 0, cntMax = 0, rule;
51         uint16_t        j;
52
53 #ifndef UART 
54         uint8_t         k, m;
55 #endif
56
57 // ---------------------- some useful defines -------------------
58
59 #define false           0
60 #define true            1
61
62 #define output_low(port,pin) port &= ~(1<<pin)
63 #define output_high(port,pin) port |= (1<<pin)
64 #define set_input(portdir,pin) portdir &= ~(1<<pin)
65 #define set_output(portdir,pin) portdir |= (1<<pin)
66
67 #define invert(port,pin) port ^= (1<<pin)
68
69 #define bit(x) (1 << (x))
70
71
72 void dly_500ns( uint16_t cnt ){
73
74         do {
75
76                 asm volatile (
77                     "    rjmp 1f"       "\n"
78                     "1:  rjmp 2f"       "\n"
79                     "2:"        "\n"
80                 );
81
82         } while (cnt--);
83 }
84
85 // ---------------------- simple UART TX -----------------------
86
87 #ifdef UART 
88
89 void put_char( uint8_t c ){
90
91         c = ~c;
92         output_low( PORTB, LED2 );
93         for( uint8_t i = 10; i; i-- ){          // 10 bits
94
95                 _delay_us( 1000000 / 9600 );    // bit duration / (8MHz, 9600, 104 us) Delay 832 cycles
96
97                 if( c & 1 ) output_low( PORTB, LED2 ); else output_high( PORTB, LED2 ); // data bit 0 / data bit 1 or stop bit
98                 c >>= 1;
99         }
100
101 /*
102 void put_str_P(PGM_P str){
103         static PGM_P s;
104         s=str;
105         while (pgm_read_byte(s)) put_char( pgm_read_byte(s++) );
106 }
107 */
108
109 void put_str(char * str){
110         while (*str) put_char( *str++ );
111 }
112
113 void put_num( uint8_t num ){
114
115         uint8_t div = 1;
116
117         for (div = 1; num / div >= 10 ; div *= 10);
118
119         do {
120                 put_char( 48 + (( num / div ) % 10 )); 
121                 div /= 10;
122         } while ( div > 0 );
123
124 }
125
126 #endif 
127
128 // ---------------------- LED PWM ------------------------------
129
130 #ifndef UART
131
132 #define INT_PWM_DISABLE ({ TIMSK0 &= ~((1<<OCIE0A) | (1<<TOIE0)); })    // compare interrupt off
133 #define INT_PWM_ENABLE  ({ TIMSK0 |= ((1<<OCIE0A) | (1<<TOIE0)); })     // compare interrupt on
134
135 ISR ( TIM0_COMPA_vect ){
136         if ( mode == MODE_BLUE || mode == MODE_BLUE_RED) output_low(PORTB, LED);
137         if ( mode == MODE_RED || mode == MODE_BLUE_RED) output_low(PORTB, LED2);
138 }
139
140 ISR ( TIM0_OVF_vect ){
141         if ( mode == MODE_BLUE || mode == MODE_BLUE_RED)  output_high(PORTB, LED);
142         if ( mode == MODE_RED || mode == MODE_BLUE_RED) output_high(PORTB, LED2);
143 }
144
145 void ledPWMInint(){
146
147         // Timer0 in mode 14, fast PWM with ICR0 as top.
148         // Enable OC0A and OC0B, lower mode bits
149 //      TCCR0A = (1<<COM0A1) | (1<<COM0B1) | (1<<WGM01);
150         TCCR0A = (1<<WGM01);
151         TIMSK0 = (1<<OCIE0A);                           // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( )
152 //      TIMSK0 = (1<<OCIE0A) | (1<<OCIE0B);             // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( ) + second interrupt for beeper
153         ICR0 = 1000;                                    // Set top to 1000
154         TCCR0B = (1<<CS01)  | (1<<WGM03) | (1<<WGM02);  // Start timer with prescaler 1:8 and set upper mode bits. WGM0 = 1110 Fast PWM ICR0 TOP TOP
155 }
156
157 #endif
158
159 // ---------------------- read ADC -----------------------------
160
161
162 void readADC(){
163
164         set_input(DDRB, ADC);   // input
165         output_low(PORTB, ADC); // no pull-up
166
167         PRR   &= ~(1<<PRADC);                                           // power on ADC
168         ADMUX = 0;                                                      // channel selection (PB0) MUX1 = 0, MUX0 = 0
169 //      ADMUX = (1<<MUX0);                                              // channel selection (PB1) MUX1 = 0, MUX0 = 1
170
171 //      _delay_ms(30);
172 //      dly_30();
173         dly_500ns((uint16_t)2000 * 30);
174
175         ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);      // enable ADC + set prescaler 128
176         ADCSRA |= (1<<ADSC);                                            // start convertion (in single mode)
177         while ((ADCSRA & (1<<ADIF))==0);                                // waiting for conversion completes flag, see ADCL register for result
178         ADCSRA = (1<<ADIF);                                             // clear flag
179
180         ADCSRA &= ~(1<<ADEN);                                           // disable ADC (for sleep mode) 
181         PRR |= (1<<PRADC);                                              // power off ADC
182
183         set_output(DDRB, ADC);  // output
184         output_low(PORTB, ADC); // no pull-up
185 }
186
187 // ---------------------- cell automation tools -----------------
188
189 void shift_left( uint8_t shift) {
190
191         while (shift--) {
192
193                 uint8_t carry = 0;
194                 uint8_t i = LEN - 1;
195
196                 do {
197                         uint8_t next = (uni[i] & 0x80) ? 1 : 0;
198                         uni[i] = carry | (uni[i] << 1);
199                         carry = next;
200
201                 } while ( i-- > 0 );
202
203                 uni[ LEN - 1 ] |= carry ;
204
205         }
206 }   
207
208 // ---------------------- for power down delay -----------------
209
210 EMPTY_INTERRUPT(WDT_vect); // clearing the flag (wake up by WDT interrupt)
211
212 // ====================== MAIN =================================
213
214 int main(void) {
215
216         // Set CPU speed by setting clock prescaler
217
218         CCP = 0xD8; // signature to unlock protection of CLKPSR
219         CLKPSR = 0; // sets the clock division factor
220
221         // disable digital inputs (for ADC and so)
222
223         DIDR0 = 0b00001111;
224
225         // IO and interrupts init
226
227         cli();
228 #ifndef UART
229         ledPWMInint(); // must be before DDR setup
230         INT_PWM_ENABLE;
231 #endif
232
233 //      // not required since doing in readADC()
234 //      set_input(DDRB, ADC);   // input
235 //      output_low(PORTB, ADC); // no pull-up
236
237         set_output(DDRB, LED);  
238         set_output(DDRB, LED2); 
239         set_output(DDRB, SND);   
240
241         sei();
242
243 #ifdef UART 
244 //      put_str_P( PSTR("^__.__^\n") );
245 //      put_str_P( PSTR("\n .:::.   .:::.\n:::::::.:::::::\n:::::::::::::::\n':::::::::::::'\n  ':::::::::'\n    ':::::'\n      ':'\n"));
246 #endif  
247
248         // ----------- initial randomizer -----------------
249
250         i = LEN -1;
251
252         while( i-- ) {
253                 readADC();
254                 uni[ i ] = pgm_read_byte( uni[ i+1 ] ) + ADCL;
255                 mode_init ^= uni[ i ] % 4;
256         }
257
258         if ( mode_init == MODE_ROTATE ) rule = 22; else rule = 30; // rule 22 look better, 30 gives shorter sequences and looks like giving longer 'tales' (test needed)s
259 //      mode_init = 0;
260
261 #ifdef UART 
262         put_str( "\n" );
263         put_str( " RND: " );
264         put_num( mode_init );
265         put_str( " RULE: " );
266         put_num( rule );
267         put_str( "\n" );
268 #endif
269
270         // ------------ cell automata -------------
271
272         while (1) {
273
274                 tmp = uni[ 0 ];
275
276                 for (i=0; i < LEN*8; i++){
277
278                         // new cell calculation
279                 
280                         if ( i > LEN * 8 - 4 ) triade = (tmp >> (LEN*8 - i) ); else triade = uni[ 0 ];
281                         if ( rule & bit(7 & triade) ) uni[ 0 ] |= bit(2); else uni[ 0 ] &= ~bit(2);
282 #ifdef UART 
283                         put_char( (( uni[ 0 ] >> 2) & 1) ? '*' : ' ');
284 #endif
285                         mode = mode_init;
286
287                         if ( mode_init != MODE_ROTATE ){ 
288                                 // long sequence check          
289
290         //                      if ( (( uni[ 0 ] >> 2) & 1) == (( uni[ 0 ] >> 3) & 1) ) { 
291                                 if ( (( uni[ 0 ] >> 2) & 0b11) == 0b00 ) { 
292
293                                         cnt++;
294
295         #ifdef UART 
296
297                                         if ( cnt > cntMax ) {
298                                                 cntMax = cnt;
299                                                 put_num( cntMax );
300                                         }
301         #else
302
303                                         // message LED + SND
304
305                                         if ( cnt >= cntMax  || cnt > LOW_STAT_THRS ) { 
306
307                                                 if ( cnt == cntMax && cnt > LOW_STAT_THRS ) mode = MODE_BLUE_RED;
308
309                                                 // ------------- glow --------------
310
311                                                 OCR0A = 1;
312                                                 m = EXP_LEN;
313
314                                                 while ( m-- ){
315                                                         OCR0A *= 2;     // fade in 
316 //                                                      _delay_ms(60);
317                                                         dly_500ns( 65535 ); // 2000 * 30
318                                                 }
319
320                                                 // beeps
321
322                                                 if ( cnt > cntMax ) {
323
324                                                         cntMax = cnt; 
325
326                                                         do {
327                                                                 m = uni[ cnt % LEN ] % 5;
328                                                                 j = 255;
329
330                                                                 while ( j-- ){
331                                                                         invert(PORTB, SND);
332
333                                                                         k = 30*(m + 1);
334
335                                                                         while ( k-- ) { 
336
337                                                                                 asm volatile ( //500ns
338                                                                                 "    rjmp 11f   \n"
339                                                                                 "11:  rjmp 12f  \n"
340                                                                                 "12:    \n"
341                                                                                 );
342                                                                         }
343                                                                 }
344
345                                                                 dly_500ns((uint16_t)2000 * 30);
346
347                                                         } while ( cnt-- );
348                                                 }
349
350                                                 m = EXP_LEN;
351
352                                                 while ( m-- ){ 
353                                                         OCR0A /= 2;     // fade out
354 //                                                      _delay_ms(60);
355                                                         dly_500ns( 65535 ); // 2000 * 30
356                                                 }
357
358                                                 // ---------------------------------
359
360                                         }
361
362         #endif
363
364                                 } else {
365                                         cnt = 0;
366                                 }
367
368                         } else { // MODE_ROTATE
369
370                                 if ( (( uni[ 0 ] >> 2) & 1) ) {
371                                         output_low(PORTB, LED); 
372                                         output_high(PORTB, LED2);
373                                 } else { 
374                                         output_high(PORTB, LED);
375                                         output_low(PORTB, LED2); 
376                                 }
377 //                              _delay_ms(6);
378                                 dly_500ns( 7300 ); // define speed of oscilation (2000 * x)
379                         }
380
381                         shift_left( 1 );
382                 }
383
384                 if ( mode_init == MODE_ROTATE ){
385
386                         output_low(PORTB, LED); 
387                         output_low(PORTB, LED2); 
388
389                         j = 10200; // length of backflight beep
390
391                         while ( j-- ){
392                                 invert(PORTB, SND);
393
394                                 asm volatile ( // 50 us
395                                     "    ldi  r18, 133" "\n"
396                                     "1:  dec  r18"      "\n"
397                                     "    brne 1b"       "\n"
398                                     "    nop"   "\n"
399                                 );
400                         }
401
402                 }
403
404 //              invert(PORTB, SND);
405
406                 shift_left( LEN*8-1 ); // 1 bit right shift, not really required if you don't visualize it, just for beauty of canonical view
407
408 #ifdef UART 
409                 put_char( ' ' );
410                 put_num( cntMax );
411 #endif
412
413                 // ---------------------------------------- 
414
415 #ifdef UART 
416
417                 readADC();
418 //              put_str_P( PSTR(" DAC: ") );
419                 put_str( " DAC: " );
420                 put_num( ADCL );
421 //              put_str_P( PSTR("\n") );
422                 put_str( "\n" );
423
424 //              _delay_us(10000);               
425 #endif
426
427 #ifndef UART 
428                 // ------------- delay --------------------
429
430                 if ( mode_init != MODE_ROTATE ) {       // disabled in Rotate Mode
431
432                         wdt_reset();
433                         wdt_enable( WDTO_8S ); // <-- delay 
434                         WDTCSR |= (1<<WDIE);
435
436                         set_sleep_mode(SLEEP_MODE_PWR_DOWN);
437                         sleep_enable();
438
439                         NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
440                                 sleep_cpu();
441                                 wdt_disable();
442                         }
443                         sleep_disable();
444
445                         // re-init 
446
447                         DIDR0 = 0b00001111; // disable digital inputs (for ADC)
448
449 //                      // not required since doing in readADC()
450 //                      set_input(DDRB, ADC);   // input
451 //                      output_low(PORTB, ADC); // no pull-up
452
453                 }
454
455                 // ----------------------------------------
456 #endif
457
458         }
459 }
460
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717